home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume11 / mush5.7 / part09 < prev    next >
Encoding:
Internet Message Format  |  1987-09-19  |  25.9 KB

  1. Subject:  v11i059:  Mail user's shell, Part09/12
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rs@uunet.UU.NET
  5.  
  6. Submitted-by: island!argv@Sun.COM (Dan Heller)
  7. Posting-number: Volume 11, Issue 59
  8. Archive-name: mush5.7/Part09
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 9 (of 12)."
  17. # Contents:  loop.c
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. if test -f 'loop.c' -a "${1}" != "-c" ; then 
  20.   echo shar: Will not clobber existing file \"'loop.c'\"
  21. else
  22. echo shar: Extracting \"'loop.c'\" \(24112 characters\)
  23. sed "s/^X//" >'loop.c' <<'END_OF_FILE'
  24. X/* loop.c     (c) copyright 1986 (Dan Heller) */
  25. X
  26. X/*
  27. X * Here is where the main loop for text mode exists. Also, all the
  28. X * history is kept here and all the command parsing and execution
  29. X * and alias expansion in or out of text/graphics mode is done here.
  30. X */
  31. X
  32. X#include "mush.h"
  33. X
  34. X#ifdef BSD
  35. X#include <sys/wait.h>
  36. X#else
  37. X#ifndef SYSV
  38. X#include <wait.h>
  39. X#endif SYSV
  40. X#endif BSD
  41. X
  42. X#define ever (;;)
  43. X#define MAXARGS        100
  44. X#define isdelimeter(c)    (index(" \t;|", c))
  45. X
  46. Xchar *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
  47. Xchar **calloc();
  48. X
  49. Xstruct history {
  50. X    int histno;
  51. X    char **argv;
  52. X    struct history *prev;
  53. X    struct history *next;
  54. X};
  55. Xstatic struct history *hist_head, *hist_tail;
  56. Xstruct history *malloc();
  57. X#define NULL_HIST    (struct history *)0
  58. X
  59. Xstatic char *last_aliased;
  60. Xstatic int hist_size, print_only;
  61. X
  62. Xdo_loop()
  63. X{
  64. X    register char *p, **argv;
  65. X    char      **last_argv = DUBL_NULL, line[256];
  66. X    int         argc, c = (iscurses - 1);
  67. X    struct history *new;
  68. X#ifdef CURSES
  69. X    int          save_echo_flg = FALSE;
  70. X#endif CURSES
  71. X
  72. X    /* catch the right signals -- see main.c for other signal catching */
  73. X    (void) signal(SIGINT, catch);
  74. X    (void) signal(SIGQUIT, catch);
  75. X    (void) signal(SIGHUP, catch);
  76. X    (void) signal(SIGTERM, catch);
  77. X    (void) signal(SIGCHLD, sigchldcatcher);
  78. X    (void) signal(SIGPIPE, SIG_IGN); /* if pager is terminated before end */
  79. X
  80. X    turnoff(glob_flags, IGN_SIGS);
  81. X    if (hist_size == 0) /* if user didn't set history in .rc file */
  82. X    hist_size = 1;
  83. X
  84. X    for ever {
  85. X    if (setjmp(jmpbuf)) {
  86. X        Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
  87. X#ifdef CURSES
  88. X        if (c > 0) { /* don't pass last command back to curses_command() */
  89. X        iscurses = TRUE;
  90. X        c = hit_return();
  91. X        }
  92. X#endif CURSES
  93. X    }
  94. X#ifdef CURSES
  95. X    if (iscurses || c > -1) {
  96. X        /* if !iscurses, we know that we returned from a curses-based
  97. X         * call and we really ARE still in curses. Reset tty modes!
  98. X         */
  99. X        if (ison(glob_flags, ECHO_FLAG)) {
  100. X        turnoff(glob_flags, ECHO_FLAG);
  101. X        echo_off();
  102. X        save_echo_flg = TRUE;
  103. X        }
  104. X        if (!iscurses) {
  105. X        iscurses = TRUE;
  106. X        c = hit_return();
  107. X        }
  108. X        if ((c = curses_command(c)) == -1 && save_echo_flg) {
  109. X        echo_on();
  110. X        turnon(glob_flags, ECHO_FLAG);
  111. X        save_echo_flg = FALSE;
  112. X        }
  113. X        continue;
  114. X    }
  115. X#endif CURSES
  116. X    clear_msg_list(msg_list);
  117. X    (void) check_new_mail();
  118. X
  119. X    /* print a prompt according to printf like format:
  120. X     * (current message, deleted, unread, etc) are found in mail_status.
  121. X     */
  122. X    mail_status(1);
  123. X    if (Getstr(line, 256, 0) > -1)
  124. X        p = line;
  125. X    else {
  126. X        if (p = do_set(set_options, "ignoreeof")) {
  127. X        if (!*p)
  128. X            continue;
  129. X        else
  130. X            p = strcpy(line, p); /* so processing won't destroy var */
  131. X        } else {
  132. X        (void) quit(0, DUBL_NULL);
  133. X        continue; /* quit may return if new mail arrives */
  134. X        }
  135. X    }
  136. X
  137. X    skipspaces(0);
  138. X    if (!*p && !(p = do_set(set_options, "newline"))) {
  139. X        (void) readmsg(0, DUBL_NULL, msg_list);
  140. X        continue;
  141. X    }
  142. X    if (!*p) /* if newline is set, but no value, then continue */
  143. X        continue;
  144. X
  145. X    /* upon error, argc = -1 -- still save in history so user can
  146. X     * modify syntax error. if !argv, error is too severe.  We pass
  147. X     * the last command typed in last_argv for history reference, and
  148. X     * get back the current command _as typed_ (unexpanded by aliases
  149. X     * or history) in last_argv.
  150. X     */
  151. X    if (!(argv = make_command(p, &last_argv, &argc)))
  152. X        continue;
  153. X    /* now save the new argv in the newly created history structure */
  154. X    if (!(new = malloc(sizeof (struct history))))
  155. X        error("can't increment history");
  156. X    else {
  157. X        new->histno = ++hist_no;
  158. X        new->argv = last_argv; /* this is the command _as typed_ */
  159. X        new->next = NULL_HIST;
  160. X        new->prev = hist_head;
  161. X        /* if first command, the tail of the list is "new" because
  162. X         * nothing is in the list.  If not the first command, the
  163. X         * head of the list's "next" pointer points to the new command.
  164. X         */
  165. X        if (hist_head)
  166. X        hist_head->next = new;
  167. X        else
  168. X        hist_tail = new;
  169. X        hist_head = new;
  170. X    }
  171. X    /*
  172. X     * truncate the history list to the size of the history.
  173. X     * Free the outdated command (argv) and move the tail closer to front.
  174. X     * use a while loop in case the last command reset histsize to "small"
  175. X     */
  176. X    while (hist_head->histno - hist_tail->histno >= hist_size) {
  177. X        hist_tail = hist_tail->next;
  178. X        free_vec(hist_tail->prev->argv);
  179. X        xfree(hist_tail->prev);
  180. X        hist_tail->prev = NULL_HIST;
  181. X    }
  182. X
  183. X    if (print_only) {
  184. X        print_only = 0;
  185. X        free_vec(argv);
  186. X    } else if (argc > -1)
  187. X        (void) do_command(argc, argv, msg_list);
  188. X    }
  189. X}
  190. X
  191. X/* make a command from "buf".
  192. X * first, expand history references. make an argv from that and save
  193. X * in last_argv (to be passed back and stored in history). After that,
  194. X * THEN expand aliases. return that argv to be executed as a command.
  195. X */
  196. Xchar **
  197. Xmake_command(start, last_argv, argc)
  198. Xregister char *start, ***last_argv;
  199. Xint *argc;
  200. X{
  201. X    register char *p, **tmp;
  202. X    char buf[BUFSIZ];
  203. X
  204. X    if (!last_argv)
  205. X    tmp = DUBL_NULL;
  206. X    else
  207. X    tmp = *last_argv;
  208. X    /* first expand history -- (here's where argc gets set)
  209. X     * pass the buffer, the history list to reference if \!* (or whatever)
  210. X     * result in static buffer (pointed to by p) -- even if history parsing is
  211. X     * ignored, do this to remove \'s behind !'s and verifying matching quotes
  212. X     */
  213. X    if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > BUFSIZ)
  214. X    return DUBL_NULL;
  215. X    /* if history was referenced in the command, echo new command */
  216. X    if (*argc)
  217. X    puts(buf);
  218. X
  219. X    /* argc may == -1; ignore this error for now but catch it later */
  220. X    if (!(tmp = mk_argv(buf, argc, 0)))
  221. X    return DUBL_NULL;
  222. X
  223. X    /* save this as the command typed */
  224. X    if (last_argv)
  225. X    *last_argv = tmp;
  226. X
  227. X    /* expand all aliases (recursively)
  228. X     * pass _this_ command (as typed and without aliases) to let aliases
  229. X     * with "!*" be able to reference the command line just typed.
  230. X     */
  231. X    if (alias_stuff(buf, *argc, tmp) == -1)
  232. X    return DUBL_NULL;
  233. X
  234. X    /* now, expand variable references and make another argv */
  235. X    if (!variable_expand(buf))
  236. X    return DUBL_NULL;
  237. X
  238. X    if (!last_argv)
  239. X    free_vec(tmp);
  240. X
  241. X    /* with everything expanded, build final argv from new buffer
  242. X     * Note that backslashes and quotes still exist. Those are removed
  243. X     * because argument final is 1.
  244. X     */
  245. X    tmp = mk_argv(buf, argc, 1);
  246. X    return tmp;
  247. X}
  248. X
  249. X/*
  250. X * do the command specified by the argument vector, argv.
  251. X * First check to see if argc < 0. If so, someone called this
  252. X * command and they should not have! make_command() will return
  253. X * an argv but it will set argc to -1 if there's a sytanx error.
  254. X */
  255. Xdo_command(argc, argv, list)
  256. Xchar **argv, list[];
  257. X{
  258. X    register char *p;
  259. X    char **tmp = argv;
  260. X    int i, status;
  261. X    long do_pipe = ison(glob_flags, DO_PIPE);
  262. X
  263. X    turnoff(glob_flags, IS_PIPE);
  264. X
  265. X    if (argc <= 0) {
  266. X    turnoff(glob_flags, DO_PIPE);
  267. X    return -1;
  268. X    }
  269. X
  270. X    clear_msg_list(list);
  271. X
  272. X    for (i = 0; do_pipe >= 0 && argc; argc--) {
  273. X    p = argv[i];
  274. X    if (!strcmp(p, "|") || !strcmp(p, ";")) {
  275. X        if (do_pipe = (*p == '|'))
  276. X        turnon(glob_flags, DO_PIPE);
  277. X        argv[i] = NULL;
  278. X        /* if piping, then don't call next command if this one fails. */
  279. X        if ((status = exec_argv(i, argv, list)) <= -1 && do_pipe) {
  280. X        print("Broken pipe.\n");
  281. X        do_pipe = -1, turnoff(glob_flags, DO_PIPE);
  282. X        }
  283. X        /* if command failed and piping, or command worked and not piping */
  284. X        if (do_pipe <= 0)
  285. X        status = 0, clear_msg_list(list);
  286. X        /* else command worked and piping: set is_pipe */
  287. X        else if (!status)
  288. X        turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
  289. X        argv[i] = p;
  290. X        argv += (i+1);
  291. X        i = 0;
  292. X    } else
  293. X        i++;
  294. X    }
  295. X    if (do_pipe >= 0)
  296. X    status = exec_argv(i, argv, list);
  297. X    Debug("freeing: "), print_argv(tmp);
  298. X    free_vec(tmp);
  299. X    turnoff(glob_flags, DO_PIPE);
  300. X    return status;
  301. X}
  302. X
  303. Xexec_argv(argc, argv, list)
  304. Xregister char **argv, list[];
  305. X{
  306. X    register int n;
  307. X
  308. X    if (!argv || !*argv || **argv == '\\' && !*++*argv) {
  309. X    if (ison(glob_flags, IS_PIPE) || ison(glob_flags, DO_PIPE))
  310. X        print("Invalid null command.\n");
  311. X    return -1;
  312. X    }
  313. X    Debug("executing: "), print_argv(argv);
  314. X
  315. X    /* if interrupted during execution of a command, return -1 */
  316. X    if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
  317. X    Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
  318. X    return -1;
  319. X    }
  320. X
  321. X    /* standard commands */
  322. X    for (n = 0; cmds[n].command; n++)
  323. X    if (!strcmp(argv[0], cmds[n].command))
  324. X        return (*cmds[n].func)(argc, argv, list);
  325. X
  326. X    /* ucb-Mail compatible commands */
  327. X    for (n = 0; ucb_cmds[n].command; n++)
  328. X    if (!strcmp(argv[0], ucb_cmds[n].command))
  329. X        return (*ucb_cmds[n].func)(argc, argv, list);
  330. X
  331. X    /* for hidden, undocumented commands */
  332. X    for (n = 0; hidden_cmds[n].command; n++)
  333. X    if (!strcmp(argv[0], hidden_cmds[n].command))
  334. X        return (*hidden_cmds[n].func)(argc, argv, list);
  335. X
  336. X#ifdef SUNTOOL
  337. X    /* check tool-only commands */
  338. X    if (istool)
  339. X    for (n = 0; fkey_cmds[n].command; n++)
  340. X        if (!strcmp(argv[0], fkey_cmds[n].command))
  341. X        return (*fkey_cmds[n].func)(argc, argv);
  342. X#endif SUNTOOL
  343. X
  344. X    if ((isdigit(**argv) || index("^.*$-`{}", **argv))
  345. X            && (n = get_msg_list(argv, list)) != 0) {
  346. X    if (n > 0 && isoff(glob_flags, DO_PIPE))
  347. X        for (n = 0; n < msg_cnt; n++)
  348. X        if (msg_bit(list, n)) {
  349. X            display_msg((current_msg = n), (long)0);
  350. X            unset_msg_bit(list, n);
  351. X        }
  352. X    return 0;
  353. X    } else if (strlen(*argv) == 1 && index("$^.", **argv)) {
  354. X    if (!msg_cnt)
  355. X        print("No messages.");
  356. X    else {
  357. X        if (**argv != '.')
  358. X        current_msg = (**argv == '$') ? msg_cnt-1 : 0;
  359. X        set_msg_bit(list, current_msg);
  360. X        display_msg(current_msg, (long)0);
  361. X    }
  362. X    return 0;
  363. X    }
  364. X    /* get_msg_list will set the current message bit if nothing parsed */
  365. X    unset_msg_bit(list, current_msg);
  366. X
  367. X    if (!istool && do_set(set_options, "unix")) {
  368. X    if (ison(glob_flags, IS_PIPE) || ison(glob_flags, DO_PIPE))
  369. X        print("There is no piping to or from UNIX commands.\n");
  370. X    else
  371. X        execute(argv);  /* try to execute a unix command */
  372. X    return -1; /* doesn't affect messages! */
  373. X    }
  374. X
  375. X    print("%s: command not found.\n", *argv);
  376. X    if (!istool)
  377. X    print("type '?' for valid commands, or type `help'\n");
  378. X    return -1;
  379. X}
  380. X
  381. X/* recursively look for aliases on a command line.  aliases may
  382. X * reference other aliases.
  383. X */
  384. Xalias_stuff(b, argc, Argv)
  385. Xregister char     *b, **Argv;
  386. X{
  387. X    register char     *p, **argv = DUBL_NULL;
  388. X    register int     n = 0, i = 0, Argc;
  389. X    static int         loops;
  390. X    int         dummy;
  391. X
  392. X    if (++loops == 20) {
  393. X    print("Alias loop.\n");
  394. X    return -1;
  395. X    }
  396. X    for (Argc = 0; Argc < argc; Argc++) {
  397. X    register char *h = Argv[n + ++i];
  398. X    register char *p2 = "";
  399. X
  400. X    /* we've hit a command separator or the end of the line */
  401. X    if (h && strcmp(h, ";") && strcmp(h, "|"))
  402. X        continue;
  403. X
  404. X    /* create a new argv containing this (possible subset) of argv */
  405. X    if (!(argv = calloc((unsigned)(i+1), sizeof (char *))))
  406. X        continue;
  407. X    while (i--)
  408. X        strdup(argv[i], Argv[n+i]);
  409. X
  410. X    if ((!last_aliased || strcmp(last_aliased, argv[0]))
  411. X            && (p = alias_expand(argv[0]))) {
  412. X        /* if history was referenced, ignore the rest of argv
  413. X         * else copy all of argv onto the end of the buffer.
  414. X         */
  415. X        if (!(p2 = hist_expand(p, argv, &dummy)))
  416. X        break;
  417. X        if (!dummy)
  418. X        (void) argv_to_string(p2+strlen(p2), argv+1);
  419. X        if (Strcpy(b, p2) > BUFSIZ) {
  420. X        print("Not enough buffer space.\n");
  421. X        break;
  422. X        }
  423. X        /* release old argv and build a new one based on new string */
  424. X        free_vec(argv);
  425. X        if (!(argv = mk_argv(b, &dummy, 0)))
  426. X        break;
  427. X        if (alias_stuff(b, dummy, argv) == -1)
  428. X        break;
  429. X    } else
  430. X        b = argv_to_string(b, argv);
  431. X    xfree(last_aliased), last_aliased = NULL;
  432. X    free_vec(argv);
  433. X    b += strlen(b);
  434. X    if (h) {
  435. X        p2 = h;
  436. X        while (++Argc < argc && (h = Argv[Argc]))
  437. X        if (strcmp(h, ";") && strcmp(h, "|"))
  438. X            break;
  439. X        b += strlen(sprintf(b, " %s ", p2));
  440. X        n = Argc--;
  441. X    }
  442. X    i = 0;
  443. X    }
  444. X    xfree(last_aliased), last_aliased = NULL;
  445. X    --loops;
  446. X    if (Argc < argc) {
  447. X    free_vec(argv);
  448. X    return -1;
  449. X    }
  450. X    return 0;
  451. X}
  452. X
  453. Xchar *
  454. Xalias_expand(cmd)
  455. Xregister char *cmd;
  456. X{
  457. X    register char *p;
  458. X    register int x;
  459. X
  460. X    if (!(p = do_set(functions, cmd)))
  461. X    return NULL;
  462. X    last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
  463. X    if (isoff(glob_flags, WARNING))
  464. X    return p;
  465. X    for (x = 0; cmds[x].command; x++)
  466. X    if (!strcmp(cmd, cmds[x].command)) {
  467. X        wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
  468. X        return p;
  469. X    }
  470. X    for (x = 0; ucb_cmds[x].command; x++)
  471. X    if (!strcmp(cmd, ucb_cmds[x].command)) {
  472. X        wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
  473. X        return p;
  474. X    }
  475. X    return p;
  476. X}
  477. X
  478. X/* expand history references and separate message lists from other tokens */
  479. Xchar *
  480. Xhist_expand(str, argv, hist_was_referenced)
  481. Xregister char *str, **argv;
  482. Xregister int *hist_was_referenced;
  483. X{
  484. X    static char   buf[BUFSIZ];
  485. X    register int  b = 0, inquotes = 0;
  486. X    int       first_space = 0, ignore_bang;
  487. X
  488. X    ignore_bang = (ison(glob_flags, IGN_BANG) ||
  489. X           do_set(set_options, "ignore_bang"));
  490. X
  491. X    if (hist_was_referenced)
  492. X    *hist_was_referenced = 0;
  493. X    while (*str) {
  494. X    while (!inquotes && isspace(*str))
  495. X        str++;
  496. X    do  {
  497. X        if (!*str)
  498. X        break;
  499. X        if (b >= BUFSIZ-1) {
  500. X        print("argument list too long.\n");
  501. X        return NULL;
  502. X        }
  503. X        if ((buf[b] = *str++) == '\'') {
  504. X        /* make sure there's a match! */
  505. X        inquotes = !inquotes;
  506. X        }
  507. X        if (!first_space && !inquotes && index("0123456789{}*$", buf[b])
  508. X                 && b && !index("0123456789{}- \t", buf[b-1])) {
  509. X        buf[b+1] = buf[b];
  510. X        buf[b++] = ' ';
  511. X        while ((buf[++b] = *str++) && index("0123456789-,${}", buf[b]))
  512. X            ;
  513. X        if (!buf[b])
  514. X            str--;
  515. X        first_space++;
  516. X        }
  517. X        /* check for (;) (|) or any other delimeter and separate it from
  518. X         * other tokens.
  519. X         */
  520. X        if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b])) {
  521. X        if (b && !isspace(buf[b-1]))
  522. X            buf[b+1] = buf[b], buf[b++] = ' ';
  523. X        b++;
  524. X        break;
  525. X        }
  526. X        /* if double-quotes, just copy byte by byte, char by char ... */
  527. X        if (buf[b] == '"') {
  528. X        int B = b;
  529. X        while ((buf[++B] = *str++) && buf[B] != '"')
  530. X            ;
  531. X        if (buf[B])
  532. X            b = B;
  533. X        else
  534. X            str--;
  535. X        b++;
  536. X        continue;
  537. X        }
  538. X        if (buf[b] == '\\') {
  539. X        if ((buf[++b] = *str) == '!')
  540. X            buf[--b] = '!';
  541. X        ++str;
  542. X        } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  543. X            && !ignore_bang) {
  544. X        char word[BUFSIZ];
  545. X        if (!(str = reference_hist(str, word, argv)))
  546. X            return NULL;
  547. X        if (hist_was_referenced)
  548. X            *hist_was_referenced = 1;
  549. X        if (strlen(word) + b >= BUFSIZ) {
  550. X            print("argument list too long.\n");
  551. X            return NULL;
  552. X        }
  553. X        b += Strcpy(&buf[b], word) - 1;
  554. X        }
  555. X        b++;
  556. X    } while (*str && !isdelimeter(*str));
  557. X    if (!inquotes)
  558. X        first_space++, buf[b++] = ' ';
  559. X    }
  560. X    buf[b] = 0;
  561. X    return buf;
  562. X}
  563. X
  564. X/*
  565. X * find mush variable references and expand them to their values.
  566. X * variables are preceded by a '$' and cannot be within single
  567. X * quotes.  Only if expansion has been made do we copy buf back into str.
  568. X * RETURN 0 on failure, 1 on success.
  569. X */
  570. Xvariable_expand(str)
  571. Xregister char *str;
  572. X{
  573. X    register int     b = 0;
  574. X    char             buf[BUFSIZ], *start = str;
  575. X    int             expanded = 0;
  576. X
  577. X    while (*str) {
  578. X    if (*str == '~' && (str == start || isspace(*(str-1)))) {
  579. X        register char *p = any(str, " \t"), *tmp;
  580. X        int x = 1;
  581. X        if (p)
  582. X        *p = 0;
  583. X        tmp = getpath(str, &x);
  584. X        /* if error, print message and return 0 */
  585. X        if (x == -1) {
  586. X        wprint("%s: %s\n", str, tmp);
  587. X        return 0;
  588. X        }
  589. X        b += Strcpy(buf+b, tmp);
  590. X        if (p)
  591. X        *p = ' ', str = p;
  592. X        else
  593. X        str += strlen(str);
  594. X        expanded = 1;
  595. X    }
  596. X    /* if single-quotes, just copy byte by byte, char by char ... */
  597. X    if ((buf[b] = *str++) == '\'') {
  598. X        while ((buf[++b] = *str++) && buf[b] != '\'')
  599. X        ;
  600. X        if (!buf[b])
  601. X        str--;
  602. X    }
  603. X    /* If $ is eoln, continue.  Variables must start with a `$'
  604. X     * and continue with {, _, a-z, A-Z or it is not a variable.      }
  605. X     */
  606. X    if (buf[b] == '$' &&
  607. X        (isalpha(*str) || *str == '{' || *str == '_'))  /* } */  {
  608. X        register char c, *p, *var, *end;
  609. X
  610. X        if (*(end = var = str) == '{')  /* } */   {
  611. X        if (!isalpha(*++str) && *str != '_') {
  612. X            print("Illegal variable name.\n");
  613. X            return 0;
  614. X        }
  615. X        if (!(end = index(var, '}'))) /* { */   {
  616. X            print("Unmatched '{'.\n"); /* } */
  617. X            return 0;
  618. X        }
  619. X        *end++ = 0;
  620. X        } else
  621. X        while (isalnum(*++end) || *end == '_')
  622. X            ;
  623. X        /* advance "str" to the next parse-point, replace the end
  624. X         * of "var" (end) with a null, and save char in `c'
  625. X         */
  626. X        c = *(str = end), *end = 0;
  627. X
  628. X        /* get the value of the variable. */
  629. X        if (p = do_set(set_options, var))
  630. X        b += Strcpy(buf+b, p);
  631. X        else {
  632. X        print("%s: undefined variable\n", var);
  633. X        return 0;
  634. X        }
  635. X        expanded = 1;
  636. X        *str = c; /* replace the null with the old character */
  637. X    } else
  638. X        b++;
  639. X    }
  640. X    buf[b] = 0;
  641. X    if (expanded) /* if any expansions were done, copy back into orig buf */
  642. X    (void) strcpy(start, buf);
  643. X    return 1;
  644. X}
  645. X
  646. X/* make an vector of space delimeted character strings out of string "str".
  647. X * place in "argc" the number of args made. If final is true, then remove
  648. X * quotes and backslants according to standard.
  649. X */
  650. Xchar **
  651. Xmk_argv(str, argc, final)
  652. Xregister char *str;
  653. Xregister int *argc;
  654. X{
  655. X    register char    *s, *p;
  656. X    register int    tmp, err = 0;
  657. X    char        *newargv[MAXARGS], **argv, *p2, c;
  658. X
  659. X    *argc = 0;
  660. X    while (*str) {
  661. X    while (isspace(*str))
  662. X        ++str;
  663. X    if (*str) {        /* found beginning of a word */
  664. X        s = p = str;
  665. X        do  {
  666. X        if (p - s >= BUFSIZ-1) {
  667. X            print("argument list too long.\n");
  668. X            return DUBL_NULL;
  669. X        }
  670. X        if ((*p = *str++) == '\\') {
  671. X            if (final && (*str == ';' || *str == '|'))
  672. X            /* make ";" look like " ;" */
  673. X            *p = ' ';
  674. X            if (*++p = *str) /* assign and compare to NULL */
  675. X            str++;
  676. X            continue;
  677. X        }
  678. X        if (p2 = index("\"'", *p)) {
  679. X            register char c2 = *p2;
  680. X            /* you can't escape quotes inside quotes of the same type */
  681. X            if (!(p2 = index(str, c2))) {
  682. X            if (final)
  683. X                print("Unmatched %c.\n", c2);
  684. X            err++;
  685. X            p2 = str;
  686. X            }
  687. X            tmp = (int)(p2 - str) + 1; /* take upto & include quote */
  688. X            (void) strncpy(p + !final, str, tmp);
  689. X            p += tmp - 2 * final; /* change final to a boolean */
  690. X            if (*(str = p2))
  691. X            str++;
  692. X        }
  693. X        } while (++p, *str && !isdelimeter(*str));
  694. X        if (c = *str) /* set c = *str, check for null */
  695. X        str++;
  696. X        *p = 0;
  697. X        if (*s) {
  698. X        newargv[*argc] = savestr(s);
  699. X        (*argc)++;
  700. X        }
  701. X        *p = c;
  702. X    }
  703. X    }
  704. X    if (!*argc)
  705. X    return DUBL_NULL;
  706. X    /* newargv[*argc] = NULL; */
  707. X    if (!(argv = calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  708. X    perror("mk_argv: calloc");
  709. X    return DUBL_NULL;
  710. X    }
  711. X    for (tmp = 0; tmp < *argc; tmp++)
  712. X    argv[tmp] = newargv[tmp];
  713. X    if (err)
  714. X    *argc = -1;
  715. X    return argv;
  716. X}
  717. X
  718. X/*
  719. X * reference previous history from syntax of str and place result into buf
  720. X * We know we've got a history reference -- we're passed the string starting
  721. X * the first char AFTER the '!' (which indicates history reference)
  722. X */
  723. Xchar *
  724. Xreference_hist(str, buf, hist_ref)
  725. Xregister char *str, *buf, **hist_ref;
  726. X{
  727. X    int        relative = *str == '-'; /* relative from current hist_no */
  728. X    int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  729. X    register char  *p, **argv = hist_ref;
  730. X    struct history *hist;
  731. X
  732. X    buf[0] = 0;
  733. X    if (index("!:$*", *str)) {
  734. X    old_hist = hist_no;
  735. X    if (*str == '!')
  736. X        str++;
  737. X    } else if (isdigit(*(str + relative)))
  738. X    str = my_atoi(str + relative, &old_hist);
  739. X    else if (!(p = hist_from_str(str, &old_hist)))
  740. X    return NULL;
  741. X    else
  742. X    str = p;
  743. X    if (relative)
  744. X    old_hist = (hist_no-old_hist) + 1;
  745. X    if (old_hist == hist_no) {
  746. X    if (!(argv = hist_ref))
  747. X        print("You haven't done anything yet!\n");
  748. X    } else {
  749. X    if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  750. X        old_hist <= 0) {
  751. X        if (old_hist <= 0)
  752. X        print("You haven't done that many commands, yet.\n");
  753. X        else
  754. X        print("Event %d %s.\n", old_hist,
  755. X            (old_hist > hist_no)? "hasn't happened yet": "expired");
  756. X        return NULL;
  757. X    }
  758. X    hist = hist_head;
  759. X    while (hist && hist->histno != old_hist)
  760. X        hist = hist->prev;
  761. X    if (hist)
  762. X        argv = hist->argv;
  763. X    }
  764. X    if (!argv)
  765. X    return NULL;
  766. X    while (argv[argend+1])
  767. X    argend++;
  768. X    lastarg = argend;
  769. X    if (*str && index(":$*", *str)) {
  770. X    int isrange;
  771. X    if (*str == ':' && isdigit(*++str))
  772. X        str = my_atoi(str, &argstart);
  773. X    if (isrange = (*str == '-'))
  774. X        str++;
  775. X    if (!isdigit(*str)) {
  776. X        if (*str == 'p')
  777. X        str++, print_only = 1;
  778. X        else if (!*str || isdelimeter(*str))
  779. X        argend = argstart;
  780. X        else {
  781. X        if (*str == '*')
  782. X            if (argv[0])
  783. X            argstart = 1, argend = ++lastarg;
  784. X            else
  785. X            argstart = 0;
  786. X        else if (*str == '$' || !isrange)
  787. X            argstart = argend;
  788. X        else
  789. X            print("%c: unknown arguement selector.\n", *str);
  790. X        str++;
  791. X        }
  792. X    } else
  793. X        str = my_atoi(str, &argend);
  794. X    }
  795. X    if (argstart > lastarg || argend > lastarg || argstart > argend) {
  796. X    print("Bad argument selector.\n");
  797. X    return NULL;
  798. X    }
  799. X    while (argstart <= argend) {
  800. X    n += Strcpy(&buf[n], argv[argstart++]);
  801. X    buf[n++] = ' ';
  802. X    }
  803. X    buf[--n] = 0;
  804. X    return str;
  805. X}
  806. X
  807. X/* find a history command that contains the string "str"
  808. X * place that history number in "hist" and return the end of the string
  809. X * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  810. X * in the second example, return the pointer to "bar"
  811. X */
  812. Xchar *
  813. Xhist_from_str(str, hist_number)
  814. Xregister char *str;
  815. Xregister int *hist_number;
  816. X{
  817. X    register char *p = NULL, c = 0;
  818. X    int       full_search = 0, len, found;
  819. X    char       buf[BUFSIZ];
  820. X    struct history *hist;
  821. X#ifndef SYSV
  822. X    extern char   *re_comp();
  823. X#else
  824. X    extern char   *regcmp();
  825. X#endif SYSV
  826. X
  827. X    if (*str == '?') {
  828. X    if (p = index(++str, '?'))
  829. X        *p++ = 0;
  830. X    else
  831. X        p = str + strlen(str);
  832. X    full_search = 1;
  833. X    } else if (*str == '{')
  834. X    if (!(p = index(str, '}'))) {   /* { */
  835. X        print("Unmatched '}'");
  836. X        return NULL;
  837. X    } else
  838. X        *p++ = 0, ++str;
  839. X    else
  840. X    p = str;
  841. X    while (*p && *p != ':' && !isspace(*p))
  842. X    p++;
  843. X    c = *p, *p = 0;
  844. X    if (*str) {
  845. X#ifndef SYSV
  846. X    if (re_comp(str))
  847. X#else
  848. X    if (!regcmp(str, NULL))
  849. X#endif SYSV
  850. X        return NULL;
  851. X    } else {
  852. X    *hist_number = hist_no;
  853. X    return p;
  854. X    }
  855. X    len = strlen(str);
  856. X    /* move thru the history in reverse searching for a string match. */
  857. X    for (hist = hist_head; hist; hist = hist->prev) {
  858. X    if (full_search) {
  859. X        (void) argv_to_string(buf, hist->argv);
  860. X        Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  861. X    }
  862. X    if (!full_search) {
  863. X        (void) strcpy(buf, hist->argv[0]);
  864. X        Debug("Checking for (%s) in (#%d: %*s)\n",
  865. X        str, hist->histno, len, buf);
  866. X        found = !strncmp(buf, str, len);
  867. X    } else
  868. X        found =
  869. X#ifndef SYSV
  870. X        re_exec(buf)
  871. X#else
  872. X        !!regex(buf, NULL) /* convert to boolean value */
  873. X#endif SYSV
  874. X                == 1;
  875. X    if (found) {
  876. X        *hist_number = hist->histno;
  877. X        Debug("Found it in history #%d\n", *hist_number);
  878. X        *p = c;
  879. X        return p;
  880. X    }
  881. X    }
  882. X    print("%s: event not found\n", str);
  883. X    return NULL;
  884. X}
  885. X
  886. Xdisp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  887. Xregister int n;
  888. Xchar **argv;
  889. X{
  890. X    register int    list_num = TRUE, num_of_hists = hist_size;
  891. X    register int    reverse = FALSE;
  892. X    struct history    *hist = hist_tail;
  893. X
  894. X    while (*++argv && *argv[0] == '-') {
  895. X    n = 1;
  896. X    do  switch(argv[0][n]) {
  897. X        case 'h': list_num = FALSE;
  898. X        when 'r': reverse = TRUE; hist = hist_head;
  899. X        otherwise: print("usage: history [-h] [-r] [#histories]\n");
  900. X               return -1;
  901. X        }
  902. X    while (argv[0][++n]);
  903. X    }
  904. X    if (*argv)
  905. X    if (!isdigit(**argv)) {
  906. X        print("history: badly formed number\n");
  907. X        return -1;
  908. X    } else
  909. X        num_of_hists = atoi(*argv);
  910. X
  911. X    if (num_of_hists > hist_size || num_of_hists > hist_no)
  912. X    num_of_hists = min(hist_size, hist_no);
  913. X
  914. X    if (!reverse)
  915. X    while (hist_no - hist->histno > num_of_hists) {
  916. X        printf("skipping %d\n", hist->histno);
  917. X        hist = hist->next;
  918. X    }
  919. X
  920. X    for (n = 0; n < num_of_hists && hist; n++) {
  921. X    if (list_num)
  922. X        wprint("%4.d  ", hist->histno);
  923. X    print_argv(hist->argv);
  924. X    hist = (reverse)? hist->prev : hist->next;
  925. X    }
  926. X    return -1;
  927. X}
  928. X
  929. Xinit_history(newsize)
  930. X{
  931. X    if ((hist_size = newsize) < 1)
  932. X    hist_size = 1;
  933. X}
  934. X
  935. END_OF_FILE
  936. if test 24112 -ne `wc -c <'loop.c'`; then
  937.     echo shar: \"'loop.c'\" unpacked with wrong size!
  938. fi
  939. # end of 'loop.c'
  940. fi
  941. echo shar: End of archive 9 \(of 12\).
  942. cp /dev/null ark9isdone
  943. MISSING=""
  944. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  945.     if test ! -f ark${I}isdone ; then
  946.     MISSING="${MISSING} ${I}"
  947.     fi
  948. done
  949. if test "${MISSING}" = "" ; then
  950.     echo You have unpacked all 12 archives.
  951.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  952. else
  953.     echo You still need to unpack the following archives:
  954.     echo "        " ${MISSING}
  955. fi
  956. ##  End of shell archive.
  957. exit 0
  958.